﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;


/// <summary>
/// 用于动态创建模块的工具。
/// </summary>
public sealed class ModuleBuilder {

    #region 节点

    /// <summary>
    /// 获取或设置当前模块的名字。
    /// </summary>
    public string name;

    /// <summary>
    /// 获取或设置当前模块的版本号。
    /// </summary>
    public Version version;

    public string aliasName {
        get {
            return _globalMember.name;
        }
        set {
            _globalMember.name = value;
        }
    }

    public SingleLinkedList<MemberBuilder.Annotation> annotations {
        get {
            return _globalMember.annotations;
        }
    }

    public ByteArrayList customAttributes {
        get {
            return _globalMember.customAttributes;
        }
        set {
            _globalMember.customAttributes = value;
        }
    }

    #endregion

    #region 依赖的模块

    /// <summary>
    /// 获取所有依赖的模块。
    /// </summary>
    SingleLinkedList<RequiredModuleInfo> _requiredModules = new SingleLinkedList<RequiredModuleInfo>();

    byte _requiredModuleCount = 0;

    /// <summary>
    /// 声明当前模块对其它模块的倚赖。
    /// </summary>
    /// <param name="moduleName"></param>
    /// <param name="version"></param>
    public void requireModule(RequiredModuleInfo rmi) {
        rmi.id = ++_requiredModuleCount;
        _requiredModules.add(rmi);
    }

    public RequiredModuleInfo requireModule(RequiredModuleInfo.RequireModuleAttribute attributes, string name, Version version) {
        var rmi = new RequiredModuleInfo();
        rmi.attributes = attributes;
        rmi.name = name;
        rmi.version = version;
        requireModule(rmi);
        return rmi;
    }

    public RequiredModuleInfo requireModule(string name) {
        var rmi = new RequiredModuleInfo();
        rmi.name = name;
        requireModule(rmi);
        return rmi;
    }

    void writeRequiredModuleInfos(MemberBuilder.WriteContext context) {
        foreach (RequiredModuleInfo rmi in _requiredModules) {
            rmi.offset = (uint)context.target.Position;

            bool nameIsUnicode = Util.isUnicode(rmi.name);
            if(nameIsUnicode) {
                rmi.attributes |= RequiredModuleInfo.RequireModuleAttribute.unicodeName;
            }
            
            context.writeShort((ushort)rmi.attributes);

            Debug.Assert(rmi.name.Length <= byte.MaxValue);
            context.writeByte((byte)rmi.name.Length);
            if (nameIsUnicode) {
                context.writeUnicodeString(rmi.name);
            } else {
                context.writeAnsiString(rmi.name);
            }

            context.writeVersion(rmi.version);
        }
    }

    void writeRequiredModuleInfoIndex(MemberBuilder.WriteContext context) {
        context.requiredModuleIndexOffset = (uint)context.target.Position - 4;
        foreach (RequiredModuleInfo rmi in _requiredModules) {
            context.writeInt(rmi.offset);
        }
    }

    #endregion

    #region 检查点

    /// <summary>
    /// 获取所有检查点。
    /// </summary>
    SingleLinkedList<Checkpoint> _checkpoints = new SingleLinkedList<Checkpoint>();

    ushort _checkPointsId;

    public void addCheckpoint(Checkpoint checkpoint) {
        checkpoint.id = ++_checkPointsId;
        _checkpoints.add(checkpoint);
    }

    public Checkpoint createCheckpoint(string path, ushort line) {
        Checkpoint p = new Checkpoint();
        p.path = path;
        p.line = line;
        addCheckpoint(p);
        return p;
    }

    void writeCheckpoints(MemberBuilder.WriteContext context) {
        foreach (Checkpoint checkpoint in _checkpoints) {
            checkpoint.offset = (uint)context.target.Position;
            context.writeUnicodeString(checkpoint.path);
            context.writeShort(checkpoint.line);
        }
    }

    void writeCheckpointIndex(MemberBuilder.WriteContext context) {
        context.checkPointIndexOffset = (uint)context.target.Position - 4;
        foreach (Checkpoint checkpoint in _checkpoints) {
            context.writeInt(checkpoint.offset);
        }
    }

    #endregion

    #region 子成员

    NamespaceBuilder _globalMember = new NamespaceBuilder() { name = String.Empty };

    public SingleLinkedList<MemberBuilder> members {
        get {
            return _globalMember.members;
        }
    }

    void writeMembers(MemberBuilder.WriteContext context) {
        context.parentMember = _globalMember;
        _globalMember.generateID(context);
        _globalMember.write(context);
    }

    void writeMemberIndex(MemberBuilder.WriteContext context) {
        context.memberIndexOffset = (uint)context.target.Position;
        _globalMember.writeIndex(context);
    }

    #endregion

    #region 生成代码

    /// <summary>
    /// 将整个模块的数据保存到指定的流。
    /// </summary>
    /// <param name="target"></param>
    /// <returns></returns>
    public bool save(Stream target) {

        // 创建写入上下文对象。
        MemberBuilder.WriteContext context = new MemberBuilder.WriteContext();
        context.target = target;

        // 第一步：写入保留文件头。
        byte[] header = Encoding.ASCII.GetBytes("TLIB\0TeaCompiler 1.0.0.0\0");
        target.Write(header, 0, header.Length);
        target.Position = 1024;

        // 第二步：写入模块表。
        writeRequiredModuleInfos(context);

        // 第三步：写入检查点列表。
        writeCheckpoints(context);

        // 第四步：写入成员列表。
        writeMembers(context);

        // 第五步：写入索引表。
        writeRequiredModuleInfoIndex(context);
        writeCheckpointIndex(context);
        writeMemberIndex(context);

        // 第六步：更新文件头。
        writeHeader(context);

        // 输出文件。
        target.Flush();

        return true;

    }

    private void writeHeader(MemberBuilder.WriteContext context) {
        uint fileSize = (uint)context.target.Position;
        context.target.Position = 512;
        context.writeByte(1);
        context.writeInt(context.requiredModuleIndexOffset);
        context.writeInt(context.checkPointIndexOffset);
        context.writeInt(context.memberIndexOffset);
        context.writeInt(fileSize);

        context.writeVersion(version);

        Debug.Assert(name.Length <= 128);
        context.writeByte((byte)name.Length);

        context.target.Position = 512 + 256;
        context.writeUnicodeString(name);


    }

    #endregion

}
